# Sinistar space ships
# See __init__.py for details on sprites.

import pyglet
import math


GOLD_SHIP = 'sprites/Pship2.tga'        # Gold player ship
DRONE_SHIP = 'sprites/Drone1.tga'       # Space invader-like ship
RED_ALIEN_SHIP = 'sprites/Cargo1.tga'   # Raspberry-like alien ship
RED_ALIEN_SHIP2 = 'sprites/Cargo1b.tga' # Damaged version of above
BLUE_SHIP = 'sprites/Cargo2.tga'        # Blue/silver ship

EXPLODE_1 = 'sprites/Explode1.tga'      # red explosion 1
EXPLODE_2 = 'sprites/Explode3.tga'      # larger red explosion
EXPLODE_3 = 'sprites/Explode5.tga'      # another red explosion
GREEN_EXPLODE = 'sprites/Explode4.tga'  # explosion of green lines


class RotatingShip:
    """ Cater for atlas sprite containing rotating ship, e.g. Pship2. """

    def __init__(self, fname, numCols):
        self._img = pyglet.image.load(fname)
        self._numDirs = numCols**2 - (numCols-2)**2
        self._tsize = self._img.width / numCols   # tile size
        self._radStep = math.pi*2 / self._numDirs

        # Make sub-regions of the texture. Ship is round the outside.
        def reg(x,y):
            y = numCols-1-y     # invert is (0,0) bottom left
            img = self._img.get_region(x=x*self._tsize, y=y*self._tsize, width=self._tsize, height=self._tsize)
            img.anchor_x = self._tsize/2
            img.anchor_y = self._tsize/2
            return img

        if numCols==3:
            self._reg = ( reg(1,0), reg(2,0),
                          reg(2,1), reg(2,2),
                          reg(1,2), reg(0,2),
                          reg(0,1), reg(0,0) )
        elif numCols==5:
            self._reg = ( reg(2,0), reg(3,0), reg(4,0),
                          reg(4,1), reg(4,2), reg(4,3), reg(4,4),
                          reg(3,4), reg(2,4), reg(1,4), reg(0,4),
                          reg(0,3), reg(0,2), reg(0,1), reg(0,0), reg(1,0) )

    image = property(lambda self: self._img)

    def getImageForDirection(self, index):
        """ If Ship is 16 images around border:
            North = 0. East = 4. South = 8. West = 12. """
        return self._reg[index]

    def getDirectionVector(self, index):
        ang = math.pi/2 - self._radStep*index
        return math.cos(ang), math.sin(ang)

    def vectorToIndex(self, x,y):
        """ Convert a direction into a sprite index. """
        ang = math.pi/2 - math.atan2(y,x)   # relative to North
        sect = int((ang + self._radStep/2) / self._radStep)
        return sect


class AnimatedSprite:
    """ Deal with an image that contains an animation as a row/grid of frames. """
    def __init__(self, fname, framesX, framesY=1):
        self._img = pyglet.image.load(fname)
        self._imgseq = pyglet.image.ImageGrid(self._img, framesY, framesX)
        self._tex = pyglet.image.TextureGrid(self._imgseq)

    def getAnimation(self, period=1.0):
        """ Create an animation that can be attached to a sprite as an image. """
        return self._tex.get_animation(period)


################################################################################
# Things we can create...
################################################################################

def resource_GoldShip():
    return RotatingShip(GOLD_SHIP, 5)

def resource_GreyAlienShip():
    return RotatingShip(DRONE_SHIP, 5)

def resource_RedAlienShip():
    return RotatingShip(RED_ALIEN_SHIP, 3)

def resource_RedAlienShipDamaged():
    return RotatingShip(RED_ALIEN_SHIP2, 3)

def resource_BlueSilverShip():
    return RotatingShip(BLUE_SHIP, 3)


def resource_Explosion1():
    return AnimatedSprite(EXPLODE_1, 8)         # medium red explosion

def resource_Explosion2():
    return AnimatedSprite(EXPLODE_2, 4, 2)      # large red explosion

def resource_Explosion3():
    return AnimatedSprite(EXPLODE_3, 4, 2)      # large red explosion

def resource_GreenExplosion():
    return AnimatedSprite(GREEN_EXPLODE, 6)   # green lines explosion


def create(name):
    return globals()['resource_' + name]()


################################################################################
# Test
################################################################################

if __name__=='__main__':

    from pyglet.gl import *     # for GL blending
    window = pyglet.window.Window()

    class ShipDemo:
        """ Demo a single directional ship. """
        def __init__(self, spr, x,y):
            self._spr = spr
            self._x, self._y = x,y
            self.update(0,0)

        def update(self, mx,my):
            mx, my = mx-self._x, my-self._y
            self._img = self._spr.getImageForDirection( self._spr.vectorToIndex(mx,my) )

        def render(self):
            self._img.blit(self._x, self._y)


    class AnimDemo:
        """ Demonstrate a single animated sprite. """
        def __init__(self, spr, x,y):
            self._spr = pyglet.sprite.Sprite(spr.getAnimation(.1))
            self._x, self._y = x,y
            self._spr.x = x
            self._spr.y = y

        def render(self):
            self._spr.draw()


    class Demo:
        """ Demonstrate all of the sprites exposed in this file. """
        def __init__(self):
            self.count = 0
            self.ships = [ShipDemo(create('GoldShip'),      100, 200),
                          ShipDemo(create('GreyAlienShip'), 200, 200),
                          ShipDemo(create('GreyAlienShip'), 100, 250),
                          ShipDemo(create('GreyAlienShip'), 250, 400),
                          ShipDemo(create('GreyAlienShip'), 100, 100),
                          ShipDemo(create('RedAlienShip'),  250, 100),
                          ShipDemo(create('RedAlienShipDamaged'),  350, 100),
                          ShipDemo(create('BlueSilverShip'),  250, 200),
                           ]
            self.anims = [ AnimDemo(create('Explosion1'), 300, 350),
                           AnimDemo(create('Explosion2'), 380, 350),
                           AnimDemo(create('Explosion3'), 460, 350),
                           AnimDemo(create('GreenExplosion'), 540, 350) ]

        def mouse(self, x,y):
            for s in self.ships:
                s.update(x,y)

        def render(self):
            for s in self.ships:
                s.render()
            for s in self.anims:
                s.render()
            self.count += 1

    demo = Demo()
    @window.event
    def on_draw():
        window.clear()
        glEnable(GL_BLEND)
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
        demo.render()

    @window.event
    def on_mouse_motion(x, y, dx, dy):
        demo.mouse(x,y)

    pyglet.app.run()
